package command;

import java.util.Collection;
import command.*;
import java.util.Set;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;

import server.KVMessage;
import server.KVMessage.StatusType;
import util.SharedStorageUtil;

public class ClientInputHandler {
	/**
	 * A list of all commands provided by the server
	 */
	public enum CommandType { PUT, GET, DELETE, LIST_KEYS, LIST_VALUES };
	
	public static final String DELIMETER = "###";
	public static final String COMMAND_PATTERN = ".*###.*###.*";
	
	private static Logger logger = Logger.getRootLogger();
	private SharedStorageUtil storageUtil;
	private ICommandInvoker commandInvoker;
	
	public ClientInputHandler() {
		storageUtil = new SharedStorageUtil();
		commandInvoker = new StorageCommandInvoker();
	}
	
	/**
	 * Handles all possible requests received from clients. Recognizes and executes commands 
	 * listen within the CommandType enum
	 * @param latestMsg
	 * @return
	 */
	public String processCommand(String msg) {
		logger.info("Input from client: "+msg);
		CommandType command = toCommandType(msg);
		System.out.println(msg);
		try {
			if(Pattern.matches(COMMAND_PATTERN, msg)) {
				int sharpsInd1 = msg.indexOf("###");
				int sharpsInd2 = msg.lastIndexOf("###");
				String parameter1 = msg.substring(sharpsInd1+3, sharpsInd2);
				String parameter2 = msg.substring(sharpsInd2+3);
				logger.info("Recognized command: "+command+" ("+parameter1+", "+parameter2+")");
				switch (command) {
					case PUT:
						return (String) commandInvoker.execute(new CommandPut(storageUtil, parameter1, parameter2));
					case GET:
						return (String) commandInvoker.execute(new CommandGet(storageUtil, parameter1));
					case DELETE:
						return (String) commandInvoker.execute(new CommandDelete(storageUtil, parameter1));
					case LIST_KEYS:
						return (String) commandInvoker.execute(new CommandListKeys(storageUtil));
					case LIST_VALUES:
						return (String) commandInvoker.execute(new CommandListValues(storageUtil));
				}
			}
		} catch (Exception exc) {
			StatusType errorStatus = getErrorStatus(command);
			logger.error("An error thrown while parsing and executing a command"+exc.getMessage());
			return composeClientResponse(errorStatus, "ERROR: " + exc.getMessage(), "");
		}
		/** If this line is reached => no command was properly executed => the input was invalid **/
		StatusType errorStatus = getErrorStatus(command);
		logger.warn("Unrecognized command");
		return composeClientResponse(errorStatus, "ERROR: invalid input", "");
	}
	
	public static CommandType toCommandType(String input) {
		input = input.toUpperCase();
		for (CommandType command: CommandType.values() ) {
			if(input.startsWith(command.name()+DELIMETER ) ) {
				return command;
			}
		}
		return null;
	}
	
	public static StatusType getErrorStatus(CommandType command) {
		StatusType[] statuses = StatusType.values();
		for (StatusType status: statuses) {
			if (status.name().equalsIgnoreCase(command+"_ERROR") ) {
				return status;
			}
		}
		return null;
	}
	
	public static String composeClientResponse(StatusType status, String parameter1, String parameter2) {
		return status+DELIMETER+parameter1+DELIMETER+parameter2;
	}
	
}
